package mt.dummy;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import mt.Order;
import mt.comm.ClientMessage;
import mt.comm.ClientSideMessage.Type;
import mt.comm.ServerComm;
import mt.comm.ServerMessage;
import mt.comm.ServerSideMessage;

/**
 * The Class ServerCommDummy.
 */
public class ServerCommDummy implements ServerComm {

	/** The Constant PORT. */
	private static final int PORT = 6000;

	/** The server socket. */
	private ServerSocket serverSocket;

	/** The socket. */
	private Socket socket;

	/** The client socket in. */
	private ObjectInputStream clientSocketIn;

	/** The client socket out. */
	private ObjectOutputStream clientSocketOut;
	// has messages type conected and newOrder
	/** The messages. */
	private BlockingQueue<ServerMessage> messages = new LinkedBlockingQueue<ServerMessage>();

	/** The clients. */
	public HashMap<String, ObjectOutputStream> clients = new HashMap<String, ObjectOutputStream>();

	/*
	 * (non-Javadoc)
	 * 
	 * @see mt.comm.ServerComm#start()
	 */
	@Override
	public void start() {

		new Thread(new Runnable() {

			@Override
			public void run() {

				try {
					// connect to port
					serverSocket = new ServerSocket(PORT);

					while (true) {
						// waiting for clients
						socket = serverSocket.accept();

						clientSocketOut = new ObjectOutputStream(
								socket.getOutputStream());
						clientSocketIn = new ObjectInputStream(
								socket.getInputStream());

						// deal with multiple clients

						new DealWithClientCommDummy(clientSocketOut,
								clientSocketIn, ServerCommDummy.this).start();

					}

				} catch (IOException e) {
					e.printStackTrace();
				} finally {

					try {

						serverSocket.close();
						clientSocketIn.close();
						clientSocketOut.close();
					} catch (IOException e) {
						// Error - maybe not closed the server socket
					}
				}

			}
		}).start();

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see mt.comm.ServerComm#hasNextMessage()
	 */
	@Override
	public boolean hasNextMessage() {
		return !messages.isEmpty();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see mt.comm.ServerComm#getNextMessage()
	 */
	@Override
	public ServerSideMessage getNextMessage() {
		System.out.println("tamanho das mensagens: " + messages.size());

		return messages.poll();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see mt.comm.ServerComm#sendError(java.lang.String, java.lang.String)
	 */
	@Override
	public void sendError(String toNickname, String error) {
		try {
			ObjectOutputStream outC = clients.get(toNickname);
			ClientMessage msg = new ClientMessage(Type.ERROR, "ERROR: " + error);
			outC.writeObject(msg);

		} catch (IOException e) {
			System.out.println("Error sending message to Client");
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see mt.comm.ServerComm#clientIsConnected(java.lang.String)
	 */
	@Override
	public boolean clientIsConnected(String nickname) {
		return (clients.containsKey(nickname));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see mt.comm.ServerComm#disconnectClient(java.lang.String)
	 */
	@Override
	public void disconnectClient(String nickname) {

		// NEED TO CLOSE CLIENT ?
		// try {
		if (clientIsConnected(nickname)) {
			clients.remove(nickname);
			ServerMessage msg = new ServerMessage(
					mt.comm.ServerSideMessage.Type.DISCONNECTED, nickname, null);
			addNewMessage(msg);
		}

		// } catch (IOException e) {
		// System.out.println("Not able to close connection correctly");
		// }
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see mt.comm.ServerComm#sendOrder(java.lang.String, mt.Order)
	 */
	@Override
	public void sendOrder(String receiversNickname, Order order) {

		try {
			// check if client is connected
			if (clientIsConnected(receiversNickname)) {
				// use same channel to send order
				ClientMessage msg = new ClientMessage(Type.ORDER, order);
				ObjectOutputStream outOrder = clients.get(receiversNickname);

				outOrder.writeObject(msg);

			}
		} catch (IOException e) {
			System.out.println("Error sending order to client");
		}
	}

	/**
	 * Adds the new client.
	 * 
	 * @param nickname
	 *            the nickname
	 * @param out
	 *            the out
	 * @param msg
	 *            the msg
	 */
	public void addNewClient(String nickname, ObjectOutputStream out,
			ServerMessage msg) {
		try {
			if (clients.containsKey(nickname)) {
				// sendError(nickname, "This nickname already exists");
				// disconnectClient(nickname);
				ClientMessage errorMsg = new ClientMessage(Type.ERROR,
						"This nickname already exists");

				out.writeObject(errorMsg);

			} else {
				clients.put(nickname, out);
				addNewMessage(msg);

			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Adds the new message.
	 * 
	 * @param message
	 *            the message
	 */

	public void addNewMessage(ServerMessage message) {
		System.out.println("arrrrrrrrS");
		messages.add(message);
	}

	/**
	 * Gets the messages.
	 * 
	 * @return the messages
	 */
	public BlockingQueue<ServerMessage> getMessages() {
		return messages;
	}

}
